//
//  PSBid.m
//  PrefCore
//
//  Created by Sergey on 1/16/14.
//  Copyright (c) 2014 Play-on-smart. All rights reserved.
//

#import "PSBid.h"
#import "PSGame.h"

@interface PSBid ()

-(instancetype)initWithType:(PSBidType)type;

-(instancetype)initPlayWithTrump:(PSCardSuit)trump andTricks:(PSTrickCount)tricks;

-(instancetype)initWithType:(PSBidType)type trump:(PSCardSuit)trump tricks:(PSTrickCount)tricks;

-(NSString*)description;

@end

@interface PSBidData : NSObject  {
    @public
    PSBid* playBids[PS_CARD_SUIT_RISING_LEN+1][PS_BID_MAX-PS_BID_MIN+1];
    PSBid* none;
    PSBid* misere;
    PSBid* whist;
    PSBid* halfWhist;
    PSBid* pass;
    PSBid* withoutThree;
    PSBid* checkTen;
}

@end

uint8_t PSGetContractIndex(PSTrickCount tricks) {
    assert(tricks >= PS_BID_MIN && tricks <= PS_BID_MAX);
    return tricks-PS_BID_MIN;
}

@implementation PSBidData

-(instancetype)init {
    if (self = [super init]) {
        none = [[PSBid alloc] initWithType:kBidNone];
        misere = [[PSBid alloc] initWithType:kBidMisere];
        whist = [[PSBid alloc] initWithType:kBidWhist];
        halfWhist = [[PSBid alloc] initWithType:kBidHalfWhist];
        pass = [[PSBid alloc] initWithType:kBidPass];
        withoutThree = [[PSBid alloc] initWithType:kBidWithoutThree];
        checkTen = [[PSBid alloc] initWithType:kBidCheckTen];
        for (PSTrickCount tricks = PS_BID_MIN ; tricks <= PS_BID_MAX ; tricks++) {
            int i = PSGetContractIndex(tricks);
            playBids[kSuitSpades][i] = [[PSBid alloc] initPlayWithTrump:kSuitSpades andTricks:tricks];
            playBids[kSuitClubs][i] = [[PSBid alloc] initPlayWithTrump:kSuitClubs andTricks:tricks];
            playBids[kSuitDiamonds][i] = [[PSBid alloc] initPlayWithTrump:kSuitDiamonds andTricks:tricks];
            playBids[kSuitHearts][i] = [[PSBid alloc] initPlayWithTrump:kSuitHearts andTricks:tricks];
            playBids[kSuitNone][i] = [[PSBid alloc] initPlayWithTrump:kSuitNone andTricks:tricks];
        }
    }
    return self;
}

@end

static PSBidData *bidData() {
    static PSBidData* instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[PSBidData alloc] init];
    });
    return instance;
}

@implementation PSBid

-(instancetype)initWithType:(PSBidType)type {
    return [self initWithType:type trump:kSuitNone tricks:0];
}

-(instancetype)initPlayWithTrump:(PSCardSuit)trump andTricks:(PSTrickCount)tricks {
    return [self initWithType:kBidPlay trump:trump tricks:tricks];
}

-(instancetype)initWithType:(PSBidType)type trump:(PSCardSuit)trump tricks:(PSTrickCount)tricks {
    if (self = [super init]) {
        _type = type;
        _trump = trump;
        _tricks = tricks;
    }
    return self;
}

+(instancetype)bidPlayWithTrump:(PSCardSuit)trump tricks:(PSTrickCount)tricks {
    NSAssert(tricks >= PS_BID_MIN, @"The lowest bid is six.");
    NSAssert(tricks <= PS_BID_MAX, @"The highest bid is ten.");
    return bidData()->playBids[trump][PSGetContractIndex(tricks)];
}

+(instancetype)bidNone {
    return bidData()->none;
}

+(instancetype)bidMisere {
    return bidData()->misere;
}

+(instancetype)bidWithoutThree {
    return bidData()->withoutThree;
}

+(instancetype)bidPass {
    return bidData()->pass;
}

+(instancetype)bidWhist {
    return bidData()->whist;
}

+(instancetype)bidHalfWhist {
    return bidData()->halfWhist;
}

+(instancetype)bidCheckTen {
    return bidData()->checkTen;
}

-(BOOL)isPlay {
    return _type == kBidPlay;
}

-(BOOL)isMisere {
    return _type == kBidMisere;
}

-(BOOL)isPass {
    return _type == kBidPass;
}

-(BOOL)isHalfWhist {
    return _type == kBidHalfWhist;
}

-(BOOL)isWhist {
    return _type == kBidWhist;
}

-(BOOL)isWihoutThree {
    return _type == kBidWithoutThree;
}

-(BOOL)isCheckTen {
    return _type == kBidCheckTen;
}

-(BOOL)isGreaterThanBid:(PSBid*)other {
    // no other bid case
    if (!other) {
        return YES;
    }
    // check pass case
    if (self.isPass) {
        return NO;
    }
    if (other.isPass) {
        return YES;
    }
    // check misere case
    if (self.isMisere && other.isMisere) {
        return NO;
    }
    if (other.isMisere) {
        return _tricks > 8;
    }
    if (self.isMisere) {
        return other.tricks < 9;
    }
    // play case: with the equal number of tricks the trump wins
    return (_tricks == other.tricks) ? _trump > other.trump : _tricks > other.tricks;
}

-(PSBid*)nextBid {
    // misere can be outbid with 9
    if (self.isMisere) {
        return [PSBid bidPlayWithTrump:kSuitSpades tricks:9];
    }
    // no trump - raise the trick number
    if (_trump == kSuitNone) {
        return _tricks != PS_BID_MAX ? [PSBid bidPlayWithTrump:kSuitSpades tricks:_tricks+1] : nil;
    }
    // otherwise go to the next trump
    return [PSBid bidPlayWithTrump:PSCardSuitNext(_trump) tricks:_tricks];
}

-(PSBid*)incrementTricks {
    return self.canIncrementTricks ? [PSBid bidPlayWithTrump:_trump tricks:_tricks+1] : self;
}

-(BOOL)canIncrementTricks {
    return self.isPlay && _tricks < PS_BID_MAX;
}

-(PSBid*)decrementTricks {
    return self.canDecrementTricks ? [PSBid bidPlayWithTrump:_trump tricks:_tricks-1] : self;
}

-(BOOL)canDecrementTricks {
    return self.isPlay && _tricks > PS_BID_MIN;
}

-(NSString*)description {
    if (_type == kBidNone) {
        return @"";
    }
    switch (_type) {
        case kBidPass:         return @"pass";
        case kBidHalfWhist:    return @"whist_half";
        case kBidWhist:        return @"whist_full";
        case kBidPlay:         return [NSString stringWithFormat:@"%u%@"
                                      ,   _tricks
                                      ,   PSCardSuitDescription(_trump)
                                      ];
        case kBidMisere:       return @"misere";
        case kBidWithoutThree: return @"without3";
        case kBidCheckTen:     return @"check10";
        default:               return @"";
    }
}

@end
